#ifndef __ALSA_CORE_H
#define __ALSA_CORE_H

#pragma once

#include "asoundlib.h"
#include "SoundManager.h"

// Definitions for Microsoft WAVE format
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
#define LE_SHORT(v) (v)
#define LE_INT(v)   (v)
#define BE_SHORT(v) bswap_16(v)
#define BE_INT(v)   bswap_32(v)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
#define LE_SHORT(v) bswap_16(v)
#define LE_INT(v)   bswap_32(v)
#define BE_SHORT(v) (v)
#define BE_INT(v)   (v)
#else
#error "Wrong endian"
#endif

#define WAV_RIFF COMPOSE_ID('R','I','F','F')
#define WAV_WAVE COMPOSE_ID('W','A','V','E')
#define WAV_FMT  COMPOSE_ID('f','m','t',' ')
#define WAV_DATA COMPOSE_ID('d','a','t','a')

// WAVE fmt block constants from Microsoft mmreg.h header
#define WAV_FMT_PCM 		0x0001
#define WAV_FMT_IEEE_FLOAT 	0x0003

#define _(msgid) gettext (msgid)
#define gettext_noop(msgid) msgid
#define N_(msgid) gettext_noop (msgid)

#ifndef LLONG_MAX
#define LLONG_MAX 				9223372036854775807LL
#endif

#define DEFAULT_FORMAT 			SND_PCM_FORMAT_S16_LE
#define DEFAULT_SPEED 			8000
#define DEFAULT_RATE			16000
#define DEFAULT_CHANNELS		1
#define FORMAT_DEFAULT 			-1
#define DEFAULT_CHUNK_SIZE		1024
#define AUDIO_BUFFER_SIZE		2048
#define FULL_AUDIO_BUFFER_SIZE	524288

#ifdef SND_CHMAP_API_VERSION
#define CONFIG_SUPPORT_CHMAP	1
#endif

#ifndef le16toh
#include <asm/byteorder.h>
#define le16toh(x) 				__le16_to_cpu(x)
#define be16toh(x) 				__be16_to_cpu(x)
#define le32toh(x) 				__le32_to_cpu(x)
#define be32toh(x) 				__be32_to_cpu(x)
#endif

#define FILTERSWEEP_LFO_CENTER 	2000.
#define FILTERSWEEP_LFO_DEPTH 	1800.
#define FILTERSWEEP_LFO_FREQ 	0.2
#define FILTER_BANDWIDTH 		50

enum {
	FORMAT_RAW,
	FORMAT_VOC,
	FORMAT_WAVE,
	FORMAT_AU
};

enum {
	OPT_VERSION = 1,
	OPT_PERIOD_SIZE,
	OPT_BUFFER_SIZE,
	OPT_DISABLE_RESAMPLE,
	OPT_DISABLE_CHANNELS,
	OPT_DISABLE_FORMAT,
	OPT_DISABLE_SOFTVOL,
	OPT_TEST_POSITION,
	OPT_TEST_COEF,
	OPT_TEST_NOWAIT,
	OPT_MAX_FILE_TIME,
	OPT_PROCESS_ID_FILE,
	OPT_USE_STRFTIME,
	OPT_DUMP_HWPARAMS,
	OPT_FATAL_ERRORS,
};

enum {
	VUMETER_NONE,
	VUMETER_MONO,
	VUMETER_STEREO
};

typedef struct params {
	snd_pcm_format_t format;
	unsigned int channels;
	unsigned int rate;
} ALSA_AUDIO_PARAMS;

typedef struct fmt_capture {
	void (*start)(void*, int fd, size_t count);
	void (*end)(void*, int fd);
	char *what;
	long long max_filesize;
} fmt_capture_t;

#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define error(...) \
		do \
		{ \
			fprintf(stderr, "%s:%d: ", __FUNCTION__, __LINE__); \
			fprintf(stderr, __VA_ARGS__); \
			putc('\n', stderr); \
		} \
		while (0)
#else
#define error(args...) \
		do \
		{ \
			fprintf(stderr, "%s:%d: ", __FUNCTION__, __LINE__); \
			fprintf(stderr, ##args); \
			putc('\n', stderr); \
		} \
		while (0)
#endif

class AlsaCore {
public:
	AlsaCore();
	virtual ~AlsaCore();

	void init_stdin(void);				// Standard IN initialized
	void done_stdin(void);				// Standard IN shutdown

	void usage(char *command);			// Display library usage info
	void device_list(void);				// Display device list
	void pcm_list(void);				// Display PCM list
	void version(void);					// Display file version

	void begin_voc(int fd, size_t cnt); // Begin VOC format
	void begin_wave(int fd, size_t cnt);// Begin WAV format
	void begin_au(int fd, size_t cnt);  // Begin AU format
	void end_voc(int fd);		        // End VOC format
	void end_wave(int fd);		        // End WAV format
	void end_au(int fd);			    // End EU format

	static void _begin_voc(void* pC, int fd, size_t cnt)  { ((AlsaCore*) pC)->begin_voc(fd, cnt); }
	static void _begin_wave(void* pC, int fd, size_t cnt) { ((AlsaCore*) pC)->begin_wave(fd, cnt); }
	static void _begin_au(void* pC, int fd, size_t cnt)   {	((AlsaCore*) pC)->begin_au(fd, cnt); }
	static void _end_voc(void* pC, int fd) 				  {	((AlsaCore*) pC)->end_voc(fd); }
	static void _end_wave(void* pC, int fd) 			  {	((AlsaCore*) pC)->end_wave(fd); }
	static void _end_au(void* pC, int fd) 				  {	((AlsaCore*) pC)->end_au(fd); }

    //todo: handle signal later
	// Signal handler recycle
    //static void signal_handler_recycle(int sig);

    //todo: handle signal later
	// Signal handler
    //static void signal_handler(int sig);

	// System signal handler recycle
	static void signal_handler_recycle(int sig);

	// System signal handler
	static void signal_handler(int sig);

	// Read from the PCM buffer
	long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames,
			size_t *max);

	// Write to the PCM buffer
	long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames);

	// Play wav/voc/au file
	void playback(char *name);

	// Play back raw PCM data
	void playbackv_go(int* fds, unsigned int channels, size_t loaded,
			off64_t count, int rtype, char **names);

	// Capture raw PCM data
	void capturev_go(int* fds, unsigned int channels, off64_t count, int rtype,
			char **names);

	// Capture raw data
	void capturev(char **names, unsigned int count);

	// Play back raw data
	void playbackv(char **names, unsigned int count);

	// Set the parameters for streaming
	int setparams_stream(snd_pcm_t *handle, snd_pcm_hw_params_t *params,
			const char *id);

	// Program exit with code
	void prg_exit(int code);

	// Suspend thread
	void suspend(void);

	// Check the Standard In
	void check_stdin(void);

	// Pause recording
	void do_pause(void);

	// Handle buffer record over/under run
	void xrun_record(void);

#ifndef check_wavefile_space
#define check_wavefile_space(buffer, len, blimit) \
		if (len > blimit) { \
			blimit = len; \
			if ((buffer = (unsigned char *)realloc(buffer, blimit)) == NULL) { \
				error(_("not enough memory")); \
				prg_exit(EXIT_FAILURE); \
			} \
		}
#endif

#ifndef timersub
#define timersub(a, b, result) \
		do { \
			(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
			(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
			if ((result)->tv_usec < 0) { \
				--(result)->tv_sec; \
				(result)->tv_usec += 1000000; \
			} \
		} while (0)
#endif

protected:
	snd_pcm_t *handle;                 	// PCM Sound driver handle
	char *pidfile_name;           		// PID file name
	int pidfile_written;        		// PID file number of units written
	char *command;                		// Executable name
	off64_t fdcount;                	// Number of files created
	int interactive;            		// Interactive on/off flag
	long term_c_lflag;           		// Terminal (Terminos) mode flags
	int fd;                     		// File handle
	int in_aborting;            		// Aborting control flag
	volatile int recycle_capture_file;  // Reuse a file flag
	int verbose;                		// Display information on/off flag
	int quiet_mode;             		// Execute without output on/off flag
	int use_poll;               		// Poll mode flag
	volatile int capture_stop;          // Capture stop interrupt flag
	int test_nowait;            		// Flag to call the PCM wait for 100 msec
	int can_pause;              		// Hardware can pause flag
	snd_pcm_stream_t stream;            // Stream handle
	snd_output_t *loghandle;            // Log output handle
	int timelimit;						// Record time limit
	int file_type;						// Type of sound file
	int open_mode;						// Mode to open PCM sound handle with
	int mmap_flag;						// Memory map access flag
	int interleaved;					// Interleave forward error correction flag
	int block;							// Blocking mode flag
	int nonblock;						// Nonblocking (anti-blocking) flag
	unsigned char *audiobuf;			// Audio buffer pointer
	snd_pcm_uframes_t chunk_size;		// Size of an audio chunk
	size_t chunk_bytes;					// Chunk byte size
	unsigned period_time;				// PCM period time
	unsigned buffer_time;				// Amount of calculated time allocated in the buffer
	snd_pcm_uframes_t period_frames;	// Period time frames
	snd_pcm_uframes_t buffer_frames;	// Buffer time frames
	int avail_min;						// Minimum available
	int start_delay;					// Start play delay
	int stop_delay;						// Delay after stop
	int monotonic;						// Mono flag
	int fatal_errors;					// Fatal error flag
	int vumeter;						// VU meter quantity
	int buffer_pos;						// Position in the buffer
	size_t bits_per_sample;				// Bits-per-sample
	size_t bits_per_frame; 				// Bits-per-frame
	int test_position;					// Flag to test buffer position or not
	int test_coef;						// Coefficient for calculating buffer size (8)
	long long max_file_size;			// Maximum WAV file size
	int max_file_time;					// Maximum time a file has available to store data
	int use_strftime;					// Use time tagging flag
	int dump_hw_params;					// Dump Hardware parameters flag
	off64_t pbrec_count;				// Play Back record count
	int vocmajor;						// VOC file major Id marker
	int vocminor;						// VOC file minor Id marker
	int resample;						// Re-sample flag
	snd_pcm_uframes_t period_size;		// PCM Period size
	int buffer_size;					// Size of stream buffer
	int latency_min;					// In frames / 2
	int latency_max;					// Maximum latency setting
	snd_output_t *output;				// Output buffer
	unsigned long loop_limit;			// Loop limit
	int loop_sec;						// Loop seconds
	int effect;							// Use Effects flag

	// Callbacks
	snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer,
			snd_pcm_uframes_t size);
	snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer,
			snd_pcm_uframes_t size);
	snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs,
			snd_pcm_uframes_t size);
	snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs,
			snd_pcm_uframes_t size);

	ALSA_AUDIO_PARAMS hwparams;
	ALSA_AUDIO_PARAMS rhwparams;

	// Filter the sweep variables
	float lfo, dlfo, fs, fc, BW, C, D, a0, a1, a2, b1, b2, *x[3], *y[3];

	static fmt_capture_t fmt_rec_table[];

#ifdef CONFIG_SUPPORT_CHMAP
	snd_pcm_chmap_t *channel_map; 		// Character map to override
	unsigned int *hw_map; 	 			// Character map to folset_play_paramslow
#endif

	// Test if VOC file
	int test_vocfile(void *buffer);

	// Test if WAV file
	ssize_t test_wavefile(int fd, unsigned char *_buffer, size_t size);

	// Test if can read a WAV file
	size_t test_wavefile_read(int fd, unsigned char *buffer, size_t *size,
			size_t reqsize, int line);

	// Test if AU file
	int test_au(int fd, void *buffer);

	// Show sample formats
	void show_available_sample_formats(snd_pcm_hw_params_t* params);

	// Setup character map
	int setup_chmap(void);

	// Setup play parameters
	void set_play_params(void);

	// Setup record parameters
	void set_record_params(void);

	// Handle buffer play over/under run
	void xrun_play(void);

	// Capture voice audio to wave file
	void capture(char *orig_name);

	// Stop recording process
	void stop_capture(void);

	// Display mono VU meter text line
	void print_vu_meter_mono(int perc, int maxperc);

	// Display stereo VU meter text line
	void print_vu_meter_stereo(int *perc, int *maxperc);

	// Display meter text line
	void print_vu_meter(signed int *perc, signed int *maxperc);

	// Peak handler
	void compute_max_peak(unsigned char *data, size_t count);

	// Test buffer position
	void do_test_position(void);

	// Re-map data in memory
	unsigned char* remap_data(unsigned char *data, size_t count);

	// Re-map data for all channels
	unsigned char** remap_datav(unsigned char **data, size_t count);

	// PCM read function
	ssize_t pcm_read(unsigned char *data, size_t rcount);

	// PCM write function
	ssize_t pcm_write(unsigned char *data, size_t count);

	// PCM write multiple channels function
	ssize_t pcm_writev(unsigned char **data, unsigned int channels,
			size_t count);

	// PCM read multiple channels function
	ssize_t pcm_readv(unsigned char **data, unsigned int channels,
			size_t rcount);

	// Read channel data
	ssize_t pcm_channel_read(unsigned char *data, size_t rcount);

	// Write VOC PCM function
	ssize_t voc_pcm_write(unsigned char *data, size_t count);

	// Write VOC silence
	void voc_write_silence(unsigned x);

	// Flush VOC PCM
	void voc_pcm_flush(void);

	// Play VOC file
	void voc_play(int fd, int ofs, char *name);

	// Play raw data
	void playback_go(int fd, size_t loaded, off64_t count, int rtype,
			char *name);

	// Initialize for RAW data
	void init_raw_data(void);

	// Calculate the data count to read from/to dsp
	off64_t calc_count(void);

	// Create wave file header
	void header(int rtype, char *name);

	// Creates a file path, like mkdir -p
	int create_path(const char *path);

	// Thread safe file open
	int safe_open(const char *name);

	// Thread safe file read
	ssize_t safe_read(int fd, void *buf, size_t count);

	// Time string formatting method
	size_t mystrftime(char *s, size_t max, const char *userformat,
			const struct tm *tm, const int filenumber);

	// Create a new file to record to
	int new_capture_file(char *name, char *namebuf, size_t namelen,
			int filecount);

	// Create and display new file time
	int new_capture_file_strftime(char *name, char *namebuf, size_t namelen,
			int filecount);

	// Set the streaming buffer size
	int setparams_bufsize(snd_pcm_t *handle, snd_pcm_hw_params_t *params,
			snd_pcm_hw_params_t *tparams, snd_pcm_uframes_t bufsize,
			const char *id);

	// Set parameters
	int setparams_set(snd_pcm_t *handle, snd_pcm_hw_params_t *params,
			snd_pcm_sw_params_t *swparams, const char *id);

	// Set streaming parameters
	int set_stream_params(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize);

	// Show statistics
	void showstat(snd_pcm_t *handle, size_t frames);

	// Show latency
	void showlatency(size_t latency);

	// Show IN max
	void showinmax(size_t in_max);

	// Get time stamp
	void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp);

	// Set the buffer type
	void setscheduler(void);

	// Calculate time difference
	long timediff(snd_timestamp_t t1, snd_timestamp_t t2);

	// Apply special effects
	void applyeffect(char* buffer, int r);
};

#endif
